package kbl.language;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;

public class Lexer {
	public final Properties languageSettings = new Properties();
	private static String VERSION = "lsmn";
	private static int MAXS = 0;
	private static String grammar = "language.l";
	private RegularExpressionParser reParser = new RegularExpressionParser();
	private NFA nfa = NFA.getInstance();
	private DFA dfa = DFA.getInstance();

	enum LexerStatus {
		REbegin, RE
	}

	public static void main(String[] args) throws IOException {
		Lexer gen = new Lexer(args);
		gen.process();

	}

	public Lexer() {
		loadResources();
	}

	public Lexer(String[] args) {

		loadResources();

		// Set all the options and pick up all the named grammar files
		//
		processArgs(args);

	}

	private void loadResources() {
		InputStream in = null;
		in = this.getClass().getResourceAsStream("language.properties");

		if (in != null) {
			try {
				languageSettings.load(in);

				VERSION = languageSettings.getProperty("version");
				grammar = languageSettings.getProperty("file");

			} catch (Exception e) {
				// Do nothing, just leave the defaults in place
			}
		}
	}

	private void processArgs(String[] args) {
		for (int i = 0; i < args.length; i++) {
			if (args[i].charAt(0) != '-') {
				// Must be the grammar file
				grammar = args[i];
			} else if (args[i].equals("-X")) {
				if (i + 1 >= args.length) {
					System.err.println("missing option; ignoring");
				} else {
					i++;
					MAXS = Integer.parseInt(args[i]);
				}
			} else if (args[i].equals("-?")) {
				help();
			}
		}
	}

	public NFAState process() {
		// Build the regular expression base on input file.
		// reBegin is where the TOKEN which the RE definition begin.
		// reEnd is the TOKEN end.
		// Use '|' bar to put all the TOKEN RE together.
		BufferedReader in = new BufferedReader(new InputStreamReader(this
				.getClass().getResourceAsStream(grammar)));
		StringBuffer re = new StringBuffer();
		StringBuffer mayend = new StringBuffer();
		CharRange cr = new CharRange(Character.MIN_VALUE, Character.MIN_VALUE);
		DFAState reBegin = dfa
				.NFA2DFA(nfa.RE2NFA(reParser
						.doReversePolishNotation("(\r?\n)*[A-Z]+( |\t|\r?\n)*:( |\t|\r?\n)*")));
		DFAState reEnd = dfa.NFA2DFA(nfa.RE2NFA(reParser
				.doReversePolishNotation("( |\t|\r?\n)*;")));
		DFAState current = reBegin;
		DFAState prev = current;
		LexerStatus status = LexerStatus.REbegin;
		int i;
		char c;
		try {
			while ((i = in.read()) != -1) {
				c = (char) i;
				prev = current;
				current = current.match(cr.set(c, c));
				switch (status) {
				case RE:
					if (current == null && !prev.isMatch()) {
						current = reEnd;
						if (mayend.length() > 0) {
							re.append(mayend);
							mayend.delete(0, mayend.length());
						}
						re.append(c);
					} else {
						if (prev.isMatch()) {
							current = reBegin;
							status = LexerStatus.REbegin;
							mayend.delete(0, mayend.length());
						} else {
							mayend.append(c);
						}
					}
					break;
				default:
					if (current == null) {
						current = reBegin;
						if (prev.isMatch()) {
							current = reEnd;
							status = LexerStatus.RE;
							if (re.length() != 0) {
								nfa.addNFA(reParser.doReversePolishNotation(re
										.toString()));
								re.delete(0, re.length());
								// re.append('|');
							}
							re.append(c);
						}
					}
					break;
				}

				// System.out.print((char) c);
			}
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		dfa.DFAcount = 0;
		return nfa.getRootNFA();
	}

	private static void help() {
		System.err.println("Version " + VERSION);
		System.err.println("usage: java language.Gen [args] file.l");
		System.err.println("  file = " + grammar);
		System.err.println("  -X   max number = " + MAXS);
	}
}